package logger

import (
	"fmt"
	"leeframe/internal/pkg/trace"
	"os"

	"github.com/gin-gonic/gin"
	"github.com/natefinch/lumberjack"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"

	"leeframe/configs"
)

var logger *zap.SugaredLogger

// SetLogs 设置日志级别、输出格式和日志文件的路径
func SetLogs(logLevel zapcore.Level, logFormat bool, fileName string) {

	encoderConfig := zapcore.EncoderConfig{
		TimeKey:        configs.Get().Log.TimeKey,
		LevelKey:       configs.Get().Log.LevelKey,
		NameKey:        configs.Get().Log.NameKey,
		CallerKey:      configs.Get().Log.CallerKey,
		MessageKey:     configs.Get().Log.MessageKey,
		StacktraceKey:  configs.Get().Log.StackTrackKey,
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    zapcore.CapitalLevelEncoder,    // 大写编码器
		EncodeTime:     zapcore.ISO8601TimeEncoder,     // ISO8601 UTC 时间格式
		EncodeDuration: zapcore.SecondsDurationEncoder, //
		EncodeCaller:   zapcore.ShortCallerEncoder,     // 短路径编码器(相对路径+行号)
		EncodeName:     zapcore.FullNameEncoder,
	}

	// 设置日志输出格式
	var encoder zapcore.Encoder
	switch logFormat {
	case configs.Get().Log.LogFormatJson:
		encoder = zapcore.NewJSONEncoder(encoderConfig)
	default:
		encoder = zapcore.NewConsoleEncoder(encoderConfig)
	}

	// 获取core tree
	core := getCoreTee(logLevel, encoder, fileName)
	// 开启文件及行号
	caller := zap.AddCaller()
	// 开启开发模式，堆栈跟踪
	development := zap.Development()
	// 构造日志
	logger = zap.New(core, caller, development, zap.AddCallerSkip(1)).Sugar()
}

func getFileName(logLevel zapcore.Level, fileName string) string {
	switch logLevel {
	case zap.DebugLevel:
		return fmt.Sprintf("%s%s.log", fileName, "debug")
	case zap.InfoLevel:
		return fmt.Sprintf("%s%s.log", fileName, "info")
	case zap.WarnLevel:
		return fmt.Sprintf("%s%s.log", fileName, "warn")
	case zap.ErrorLevel:
		return fmt.Sprintf("%s%s.log", fileName, "error")
	case zap.DPanicLevel:
		return fmt.Sprintf("%s%s.log", fileName, "dpanic")
	case zap.PanicLevel:
		return fmt.Sprintf("%s%s.log", fileName, "panic")
	case zap.FatalLevel:
		return fmt.Sprintf("%s%s.log", fileName, "fatal")
	default:
		return fmt.Sprintf("%s%s.log", fileName, "info")
	}
}

// 获取core
func getCore(logLevel zapcore.Level, encoder zapcore.Encoder, fileName string) zapcore.Core {
	// 添加日志切割归档功能
	fileName = getFileName(logLevel, fileName)
	hook := lumberjack.Logger{
		Filename:   fileName,                     // 日志文件路径
		MaxSize:    configs.Get().Log.MaxSize,    // 每个日志文件保存的最大尺寸 单位：M
		MaxBackups: configs.Get().Log.MaxBackups, // 日志文件最多保存多少个备份
		MaxAge:     configs.Get().Log.MaxAge,     // 文件最多保存多少天
		Compress:   true,                         // 是否压缩
	}

	return zapcore.NewCore(
		encoder, // 编码器配置
		zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stderr), zapcore.AddSync(&hook)), // 打印到控制台和文件
		zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
			return lvl == logLevel
		}),
	)
}

func getCoreTee(logLevel zapcore.Level, encoder zapcore.Encoder, fileName string) zapcore.Core {
	debugCore := getCore(zap.DebugLevel, encoder, fileName)

	infoCore := getCore(zap.InfoLevel, encoder, fileName)

	warnCore := getCore(zap.WarnLevel, encoder, fileName)

	errorCore := getCore(zap.ErrorLevel, encoder, fileName)

	dpanicCore := getCore(zap.DPanicLevel, encoder, fileName)

	panicCore := getCore(zap.PanicLevel, encoder, fileName)

	fatalCore := getCore(zap.FatalLevel, encoder, fileName)

	switch logLevel {
	case zap.DebugLevel:
		return zapcore.NewTee(debugCore, infoCore, warnCore, errorCore, dpanicCore, panicCore, fatalCore)
	case zap.InfoLevel:
		return zapcore.NewTee(infoCore, warnCore, errorCore, dpanicCore, panicCore, fatalCore)
	case zap.WarnLevel:
		return zapcore.NewTee(warnCore, errorCore, dpanicCore, panicCore, fatalCore)
	case zap.ErrorLevel:
		return zapcore.NewTee(errorCore, dpanicCore, panicCore, fatalCore)
	case zap.DPanicLevel:
		return zapcore.NewTee(dpanicCore, panicCore, fatalCore)
	case zap.PanicLevel:
		return zapcore.NewTee(panicCore, fatalCore)
	case zap.FatalLevel:
		return zapcore.NewTee(fatalCore)
	default:
		return zapcore.NewTee(infoCore, warnCore, errorCore, dpanicCore, panicCore, fatalCore)
	}
}

func Error(args ...interface{}) {
	logger.Error(args...)
}

func ErrorN(ctx *gin.Context, args ...interface{}) {
	logger.With(zap.String(trace.TraceIDKey, ctx.GetString(trace.HeaderKey))).Error(args...)
}

func Info(args ...interface{}) {
	logger.Info(args...)
}

func InfoN(ctx *gin.Context, args ...interface{}) {
	logger.With(zap.String(trace.TraceIDKey, ctx.GetString(trace.HeaderKey))).Info(args...)
}

func Debug(args ...interface{}) {
	logger.Debug(args...)
}

func DebugN(ctx *gin.Context, args ...interface{}) {
	logger.With(zap.String(trace.TraceIDKey, ctx.GetString(trace.HeaderKey))).Debug(args...)
}

func Warn(args ...interface{}) {
	logger.Warn(args...)
}

func WarnN(ctx *gin.Context, args ...interface{}) {
	logger.With(zap.String(trace.TraceIDKey, ctx.GetString(trace.HeaderKey))).Warn(args...)
}

func Panic(args ...interface{}) {
	logger.Panic(args...)
}

func PanicN(ctx *gin.Context, args ...interface{}) {
	logger.With(zap.String(trace.TraceIDKey, ctx.GetString(trace.HeaderKey))).Panic(args...)
}

func Fatal(args ...interface{}) {
	logger.Fatal(args...)
}

func FatalN(ctx *gin.Context, args ...interface{}) {
	logger.With(zap.String(trace.TraceIDKey, ctx.GetString(trace.HeaderKey))).Fatal(args...)
}

func GetLogger() *zap.Logger {
	return logger.Desugar()
}
